/*
 * PongPhysics.hpp
 *
 *  Created on: Jun 4, 2009
 *      Author: asantos
 */

#ifndef PONGPHYSICS_HPP_
#define PONGPHYSICS_HPP_

#include <math.h>

class PongPhysical: public IPhysical{
private:
	Vector3 velocity;
	float width;
	float height;
public:
	PongPhysical(float width, float height, const Vector3 velocity = Vector3()):velocity(velocity),width(width),height(height){}
	Vector3& GetVelocity(){
		return velocity;
	}
	const Vector3& GetVelocity()const{
		return velocity;
	}
	float GetWidth()const{
		return width;
	}
	float GetHeight()const{
		return height;
	}
};


class PongPhysics{
public:
	typedef void(CollisionHandler)();
private:
	SignalBroker& signalbroker;
	boost::signals::connection getselectedscenegraphconnection;

	bool initialized;
	SceneNode* player1node;
	SceneNode* player2node;
	SceneNode* ballnode;
public:
	PongPhysics(SignalBroker& signalbroker):
		signalbroker(signalbroker)
		{
		Signal<SceneGraphController::GetSelectedSceneGraphHandler>& getselectedscenegraph = signalbroker.CreateOrGetExistingSignal<SceneGraphController::GetSelectedSceneGraphHandler>("/scenegraphcontroller/getselectedscenegraph");
		getselectedscenegraphconnection = getselectedscenegraph.connect(boost::bind(&PongPhysics::GetSelectedSceneGraph, this, _1));

		Signal<ClockView::TickHandler>& clocktick = signalbroker.CreateOrGetExistingSignal<ClockView::TickHandler>("/clock/tick");
		clocktick.connect(boost::bind(&PongPhysics::Update, this, _1, _2));

	}
	void GetSelectedSceneGraph(SceneGraph& scenegraph){
		try{
			ballnode = scenegraph.GetRoot().GetChildNodePtrByName("ball");
			player1node = scenegraph.GetRoot().GetChildNodePtrByName("paddle0");
			player2node = scenegraph.GetRoot().GetChildNodePtrByName("paddle1");

			getselectedscenegraphconnection.disconnect();

			ballnode->AddSceneNodeProperty("physical", new PhysicsProperty(*(new PongPhysical(20.0f, 20.0f))));
			player1node->AddSceneNodeProperty("physical", new PhysicsProperty(*(new PongPhysical(10.0f, 80.0f))));
			player2node->AddSceneNodeProperty("physical", new PhysicsProperty(*(new PongPhysical(10.0f, 80.0f))));

			initialized = true;
		}catch(...){

		}
	}

	void Update(long t, long dt){
		if(initialized){
			float dtf = dt/1000.0f;

			PhysicsProperty& ballphysical = ballnode->GetSceneNodeProperty<PhysicsProperty>("physical");
			PositionProperty& ballposition = ballnode->GetSceneNodeProperty<PositionProperty>("position");

			/*Update ball*/
			Matrix4& mball = ballposition.GetPosition();
			PongPhysical& pball = *dynamic_cast<PongPhysical*>(ballphysical.GetPhysicalPtr());
			Vector3& vball = pball.GetVelocity();
			mball(0,3)+=vball[0] * dtf;
			mball(1,3)+=vball[1] * dtf;

			PhysicsProperty& player1physical = player1node->GetSceneNodeProperty<PhysicsProperty>("physical");
			PositionProperty& player1position = player1node->GetSceneNodeProperty<PositionProperty>("position");

			PhysicsProperty& player2physical = player2node->GetSceneNodeProperty<PhysicsProperty>("physical");
			PositionProperty& player2position = player2node->GetSceneNodeProperty<PositionProperty>("position");

			Matrix4& mplayer1 = player1position.GetPosition();
			PongPhysical& pplayer1 = *dynamic_cast<PongPhysical*>(player1physical.GetPhysicalPtr());

			Matrix4& mplayer2 = player2position.GetPosition();
			PongPhysical& pplayer2 = *dynamic_cast<PongPhysical*>(player2physical.GetPhysicalPtr());


			/*Collision detection*/
			bool ballandpaddle0collision = false;
			bool ballandpaddle1collision = false;
			bool ballandleftcollision = false;
			bool ballandrightcollision = false;
			bool ballandtopcollision = false;
			bool ballandbottomcollision = false;
			//Ball <-> paddle0
			//right edge of ball to the right of left edge of player1paddle?
			if(mball(0,3) + pball.GetWidth()/2.0f > mplayer1(0,3) - pplayer1.GetWidth()/2.0f){
				//bottom edge of ball is below the top edge of player1paddle?
				if(mball(1,3) + pball.GetHeight()/2.0f > mplayer1(1,3) - pplayer1.GetHeight()/2.0f){
					//top edge of ball is above the bottom edge of player1paddle?
					if(mball(1,3) - pball.GetHeight()/2.0f < mplayer1(1,3) + pplayer1.GetHeight()/2.0f){
						//assume that the ball and paddle are intersecting
						ballandpaddle0collision = true;
					}
				}
			}


			//paddle1 <-> Ball
			//left edge of ball to the right of left edge of player2paddle?
			if(mball(0,3) - pball.GetWidth()/2.0f < mplayer2(0,3) + pplayer2.GetWidth()/2.0f){
				//bottom edge of ball is below the top edge of player2paddle?
				if(mball(1,3) + pball.GetHeight()/2.0f > mplayer2(1,3) - pplayer2.GetHeight()/2.0f){
					//top edge of ball is above the bottom edge of player2paddle?
					if(mball(1,3) - pball.GetHeight()/2.0f < mplayer2(1,3) + pplayer2.GetHeight()/2.0f){
						//assume that the ball and paddle are intersecting
						ballandpaddle1collision = true;
					}
				}
			}

			//Ball <-> right wall
			if(mball(0,3) + pball.GetWidth()/2.0f > 640){
				ballandrightcollision = true;
			}

			//left wall <-> ball
			if(mball(0,3) - pball.GetWidth()/2.0f < 0){
				ballandleftcollision = true;
			}


			//Ball <-> top wall
			if(mball(1,3) - pball.GetHeight()/2.0f < 0){
				ballandtopcollision = true;
			}

			//Ball <-> bottom wall
			if(mball(1,3) + pball.GetHeight()/2.0f > 480){
				ballandbottomcollision = true;
			}

			/*Collision response*/
			if(ballandpaddle0collision){
				vball[0]*= -1.0f;
				vball[1]+= mball(1,3) - mplayer1(1,3);
				mball(0,3)= mplayer1(0,3) - pplayer1.GetWidth()/2.0f - pball.GetWidth()/2.0f;//fire ballandleftwallcollision event
				signalbroker.InvokeSignal<PongPhysics::CollisionHandler>("/physics/ballandpaddle0collision");
			}else if(ballandpaddle1collision){
				vball[0]*= -1.0f;
				vball[1]+= mball(1,3) - mplayer2(1,3);
				mball(0,3)= mplayer2(0,3) + pplayer2.GetWidth()/2.0f + pball.GetWidth()/2.0f;//fire ballandleftwallcollision event
				signalbroker.InvokeSignal<PongPhysics::CollisionHandler>("/physics/ballandpaddle1collision");
			}else if(ballandleftcollision){
				vball[0]*= -1.0f;
				mball(0,3) = pball.GetWidth()/2.0f;
				//fire ballandleftwallcollision event
				signalbroker.InvokeSignal<PongPhysics::CollisionHandler>("/physics/ballandleftwallcollision");
			}else if(ballandrightcollision){
				vball[0]*= -1.0f;
				mball(0,3) = 640 - pball.GetWidth()/2.0f;
				//fire ballandrightwallcollision event
				signalbroker.InvokeSignal<PongPhysics::CollisionHandler>("/physics/ballandrightwallcollision");
			}else if(ballandtopcollision){
				vball[1]*= -1.0f;
				mball(1,3) = pball.GetHeight()/2.0f;//fire ballandleftwallcollision event
				signalbroker.InvokeSignal<PongPhysics::CollisionHandler>("/physics/ballandtopwallcollision");
			}else if(ballandbottomcollision){
				vball[1]*= -1.0f;
				mball(1,3) = 480 - pball.GetHeight()/2.0f;//fire ballandleftwallcollision event
				signalbroker.InvokeSignal<PongPhysics::CollisionHandler>("/physics/ballandbottomwallcollision");
			}

		}
	}

};
#endif /* PONGPHYSICS_HPP_ */
